#pragma once
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<unistd.h>

#include<iostream>
#include<string>
#include<cstring>

#include"log.hpp"
#include"protocol.hpp"

static const int default_fd=-1;
static const int default_retry_interval=1;
static const int default_retry_count=5;
#define CONV(addr) (struct sockaddr*)(addr)

enum ExitCode{
    USAGE_ERROR=1,
    SOCKET_ERROR,
    CONNECT_ERROR
};

enum class CONNECT_STATUS
{
    NEW,            //新创建的client
    CONNECTING,     //正在链接
    CONNECTED,      //链接成功
    DECONNECTED,    //链接失败
    CLOSE           //关闭链接
};

class Client_Connection{
public:
    Client_Connection(uint16_t port,std::string& addr):_port(port),_addr(addr)
    {
        _sockfd=default_fd;
        _status=CONNECT_STATUS::NEW;
        _retry_interval=default_retry_interval;
        _retry_max_count=default_retry_count;
    }
    void Connect()          //发起链接
    {
        //创建socket
        _sockfd=socket(AF_INET,SOCK_STREAM,0);
        if(_sockfd<0){
            LOG(FATAL)<<"socket创建失败:"<<strerror(errno);
            exit(SOCKET_ERROR);
        }
        struct sockaddr_in server;
        server.sin_family=AF_INET;
        server.sin_port=htons(_port);
        server.sin_addr.s_addr=inet_pton(AF_INET,_addr.c_str(),&server.sin_addr);

        //链接
        int n=::connect(_sockfd,CONV(&server),sizeof(server));
        if(n==-1){  //链接失败
            std::cout<<"链接失败"<<std::endl;
            _status=CONNECT_STATUS::DECONNECTED;
            return ;
        }
        _status= CONNECT_STATUS::CONNECTED;
    }
    void reconect()         //发起重连
    {
        for(int cnt=0;cnt<_retry_max_count;cnt++){
            std::cout<<"尝试重连中，重连次数："<<cnt+1<<std::endl;
            ::close(_sockfd);
            Connect();
            if(_status==CONNECT_STATUS::CONNECTED){
                std::cout<<"重连成功"<<std::endl;
                return;
            }
            sleep(_retry_interval);
        }
        _status=CONNECT_STATUS::CLOSE;  //一直重连不成功，那就不连了，直接关闭
    }
    void process()          //运行客户端
    {
        //向服务器发送数据
        int x,y;
        char oper;
        std::string send_message;
        std::cout<<"X=:";
        std::cin>>x;
        std::cout<<"Y=:";
        std::cin>>y;
        std::cout<<"oper=:";
        std::cin>>oper;
    
        request client_rq(x,y,oper);
        //1.序列化请求+编码
        client_rq.Serialize(send_message);
        encode(send_message);

        std::cout<<send_message<<std::endl;
    
        ::send(_sockfd,send_message.c_str(),send_message.size(),0);
        //接收服务器数据
        char recv_buf[1024];

        std::cout<<send_message<<std::endl;

        int n=::recv(_sockfd,recv_buf,sizeof(recv_buf)-1,0);

        std::cout<<n<<std::endl;

        if(n==0) {
            _status=CONNECT_STATUS::DECONNECTED;
            return;
        }
        recv_buf[n]=0;
        std::cout<<recv_buf<<std::endl;
        //2.解包并反序列化服务器应答
        std::string recv_message=recv_buf;

        std::cout<<recv_message<<std::endl;

        if(recv_message.empty()){
            LOG(INFO)<<"请求无响应";
            _status=CONNECT_STATUS::DECONNECTED;
            return;
        }
        std::string respond_str;
        decode(recv_message,&respond_str);
        respond client_rp;
        client_rp.DeSerialize(respond_str);

        if(client_rp.CODE()==DIV_ZERO_ERROR){
            std::cout<<"被除数不能为0";
        }
        else if(client_rp.CODE()==OPER_ERROR){
            std::cout<<"不支持当前符号操作";
        }
        else if(client_rp.CODE()==0){
            client_rp.display();
        }
    }
    void close()            //关闭链接
    {
        if(_sockfd!=default_fd){
            ::close(_sockfd);
        }
        std::cout<<"无法连接服务器，请检查网络"<<std::endl;
        exit(0);
    }
    CONNECT_STATUS status(){return _status;}
    ~Client_Connection()=default;
private:
    int _sockfd;            
    uint16_t _port;         //服务器端口号
    std::string _addr;      //服务器的IP地址
    CONNECT_STATUS _status; //客户端的链接状态
    int _retry_interval;    //重连时间间隔
    int _retry_max_count;   //最大重连次数
};

class client{
    public:
    client(uint16_t port,std::string& addr):_con(port,addr){
    }
    void excute(){
        while(true){
            switch(_con.status()){
            case CONNECT_STATUS::NEW:
                _con.Connect();
                break;
            case CONNECT_STATUS::CONNECTED:
                _con.process();
                break;
            case CONNECT_STATUS::DECONNECTED:
                _con.reconect();
                break;
            case CONNECT_STATUS::CLOSE:
                _con.close();
                return;
                break;
            default:
                break;
            }
        }
    }
    ~client(){
        _con.close();
    }
private:
    Client_Connection _con;
};
